/*
* Copyright (c) 2025 Huawei Device Co., Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including but not limited to the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to provide persons with access to the Software
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGE OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

import { InteractionEventType, PointerState, PointerOptions, GestureHandlers, FingerInfo } from '../../types/pointerGestureManager/index';
import { getSwipeDirection, getAngle, getPointersCenter, getPointersDistance, getVelocityValue } from '../../utils/determineDirection';

class InteractionEvents {
  private element: HTMLElement;
  private handlers: GestureHandlers;
  private defaultConfig?: PointerOptions;
  private config: Required<PointerOptions> = {
    clickTimeout: 250,
    doubleTapThreshold: 300,
    longPressThreshold: 500,
    speed: 0.1,
    enableClick: false,
    enableDoubleClick: false,
    enableLongPress: false,
    enableDrag: false,
    enableSwipe: false,
    enableScroll: false,
    enableScale: false,
    enableHover: false,
    enableContextMenu: false,
    enableRotate: false,
    distanceThreshold: 2 ^ (31 - 1),
    direction: 'All',
    scrollDirection: 'All',
    scrollDistance: 5,
    scaleDistance: 5,
    angle: 1
  };

  private pointerId: number | null = null;
  private isPointerDown: boolean = false;
  private lastClickTime = 0;
  private lastlongPressTime = 0;
  private longPressTimer: number | null = null;
  private scaleTimer: number | null = null;
  private isPressStart: boolean = false;
  private isPinching = false;
  private isLongPress = true;
  private pinchStart = false;
  private rotateStart = false;
  private startAngle = 0;
  private isHoverStart = false;
  private executeZoom = true;
  private executeRotate = true;
  private startX = 0;
  private startY = 0;
  private lastX = 0;
  private lastY = 0;
  private lastTime = 0;
  private startDistance = 0;
  private longPressDistance = 15;
  private clickDownTime = 800;
  private initialScale = window.devicePixelRatio;
  private updateScale = 0;
  private isDoubleFingers: boolean = false;
  private lastPointers: PointerEvent[] = [];
  private movePointers: PointerEvent[] = [];

  constructor(target: HTMLElement, handlers: GestureHandlers, defaultConfig?: PointerOptions) {
    this.element = target;
    this.handlers = handlers;
    this.defaultConfig = defaultConfig ? defaultConfig : {};
    this.config = { ...this.config, ...defaultConfig };
    if (this.config.distanceThreshold <= 0) {
      this.config.distanceThreshold = 2 ^ (31 - 1);
    }
    if (this.config.longPressThreshold <= 0) {
      this.config.longPressThreshold = 500;
    }
    if (this.config.angle <= 0 || this.config.angle > 360) {
      this.config.angle = 1;
    }
    this.setupEventListeners();
  }

  private setupEventListeners(): void {
    this.element.addEventListener('pointerdown', this.handlePointerDown);
    this.element.addEventListener('pointermove', this.handlePointerMove);
    this.element.addEventListener('pointerup', this.handlePointerUp);
    this.element.addEventListener('pointercancel', this.handlePointerCancel);
    this.element.addEventListener('touchstart', this.handleTouchStart);
    this.element.addEventListener('touchmove', this.handleTouchMove);
    this.element.addEventListener('touchend', this.handleTouchEnd);
    this.element.addEventListener('contextmenu', this.handleContextmenu);
    this.element.addEventListener('pointerover', this.handlePointerover);
    this.element.addEventListener('pointerout', this.handlePointerout);
    if (this.config.enableScroll || this.config.enableSwipe || this.config.enableScale) {
      this.element.addEventListener('wheel', this.handleWheel);
    }
    if (this.config.enableDrag) {
      if (this.handlers?.onDragStart) {
        this.element.addEventListener('dragstart', this.handleDragStart);
      }
      if (this.handlers?.onDragEnter) {
        this.element.addEventListener('dragenter', this.handleDragEnter);
      }
      if (this.handlers?.onDragMove) {
        this.element.addEventListener('dragover', this.handleDragMove);
      }
      if (this.handlers?.onDragLeave) {
        this.element.addEventListener('dragleave', this.handleDragLeave);
      }
      if (this.handlers?.onDrop) {
        this.element.addEventListener('drop', this.handleDrop);
      }
    }
  }

  private cleanupEventListeners(): void {
    this.element.removeEventListener('pointerdown', this.handlePointerDown);
    this.element.removeEventListener('pointermove', this.handlePointerMove);
    this.element.removeEventListener('pointerup', this.handlePointerUp);
    this.element.removeEventListener('pointercancel', this.handlePointerCancel);
    this.element.removeEventListener('touchstart', this.handleTouchStart);
    this.element.removeEventListener('touchmove', this.handleTouchMove);
    this.element.removeEventListener('touchend', this.handleTouchEnd);
    this.element.removeEventListener('wheel', this.handleWheel);
    this.element.removeEventListener('contextmenu', this.handleContextmenu);
    this.element.removeEventListener('pointerover', this.handlePointerover);
    this.element.removeEventListener('pointerout', this.handlePointerout);
    if (this.config.enableDrag) {
      if (this.handlers?.onDragStart) {
        this.element.removeEventListener('dragstart', this.handleDragStart);
      }
      if (this.handlers?.onDragEnter) {
        this.element.removeEventListener('dragenter', this.handleDragEnter);
      }
      if (this.handlers?.onDragMove) {
        this.element.removeEventListener('dragover', this.handleDragMove);
      }
      if (this.handlers?.onDragLeave) {
        this.element.removeEventListener('dragleave', this.handleDragLeave);
      }
      if (this.handlers?.onDrop) {
        this.element.removeEventListener('drop', this.handleDrop);
      }
    }
  }

  private handleContextmenu = (e: MouseEvent | TouchEvent): void => {
    if (this.config.enableContextMenu && this.handlers?.onContextMenu) {
      this.handlers.onContextMenu(e);
    }
  };

  private handlePointerover = (e: PointerEvent): void => {
    if (e.pointerType === 'touch') {
      return;
    }
    if ('buttons' in e && e.buttons === 0 && this.config.enableHover) {
      this.triggerHover(e);
    }
  };

  private handlePointerout = (e: PointerEvent): void => {
    if (e.pointerType === 'touch') {
      return;
    }
    if ('buttons' in e && e.buttons === 0 && this.config.enableHover) {
      this.triggerHover(e);
    }
  };

  private handlePointerDown = (e: PointerEvent): void => {
    if ((this.pointerId !== null && this.pointerId !== e.pointerId) || ('buttons' in e && e.button === 2) || e.pointerType === 'touch') {
      return;
    }
    this.lastTime = Date.now();
    this.lastlongPressTime = Date.now();
    this.isLongPress = true;
    this.pointerId = e.pointerId;
    this.isPointerDown = true;
    this.startX = this.lastX = e.clientX;
    this.startY = this.lastY = e.clientY;
    if (this.config.enableLongPress && this.handlers?.onLongPressStart) {
      this.longPressTimer = window.setTimeout(() => {
        if (!this.isPinching && !this.rotateStart && this.isLongPress) {
          this.triggerLongPressStart(e);
        }
      }, this.config.longPressThreshold);
    }
    this.lastPointers.push(e);
  };

  private handlePointerMove = (e: PointerEvent): void => {
    if (e.pointerType === 'touch') {
      return;
    }
    if ('buttons' in e && e.buttons === 0) {
      if (this.config.enableHover && this.isHoverStart) {
        this.triggerHoverMove(e);
      }
      this.isPointerDown = false;
    }
    const now = Date.now();
    const deltaTime = now - this.lastTime;
    const deltaX = e.clientX - this.lastX;
    const deltaY = e.clientY - this.lastY;
    const speedX = deltaX / deltaTime;
    const speedY = deltaY / deltaTime;
    const distance = Math.sqrt(
      Math.pow(e.clientX - this.startX, 2) +
      Math.pow(e.clientY - this.startY, 2)
    );
    const speed = distance / deltaTime;
    const isScrollMinDistance = distance >= this.config.scrollDistance;
    // 触发滚动
    if (this.config.enableScroll && this.handlers?.onScroll && isScrollMinDistance && this.isPointerDown) {
      this.triggerScroll(e, deltaX, deltaY, speedX, speedY, speed);
    }
    // 触发长按
    if (this.config.enableLongPress && this.handlers?.onLongPressStart && this.isPointerDown) {
      if (distance < this.longPressDistance) {
        this.isLongPress = true;
      } else {
        this.isLongPress = false;
      }
    }
    this.lastX = e.clientX;
    this.lastY = e.clientY;
    this.lastTime = now;
  };

  private handleLongPressEnd(e: PointerEvent, isLongPressEndTime: boolean, isDistance: boolean): void {
    if (this.config.enableLongPress && this.handlers?.onLongPressEnd && isLongPressEndTime && isDistance && this.isPressStart) {
      this.triggerLongPressEnd(e);
    }
  }

  private handleClickEvent(e: PointerEvent, distance: number, now: number, deltaTime: number): void {
    const isClickMaxDistance = distance < this.config.distanceThreshold;
    const isDoubleClickTime = now - this.lastClickTime < this.config.doubleTapThreshold;
    const isClickTime = deltaTime < this.clickDownTime;
    if (this.config.enableDoubleClick && this.handlers?.onDoubleClick && isDoubleClickTime && isClickMaxDistance) {
      this.triggerDoubleClick(e);
      this.lastClickTime = 0;
    } else if (this.config.enableClick && this.handlers?.onClick && isClickMaxDistance && isClickTime) {
      this.lastClickTime = now;
      if (this.lastClickTime === now) {
        this.triggerClick(e);
      }
    }
  }

  private handlePointerUp = (e: PointerEvent): void => {
    if (e.pointerType === 'touch' || ('buttons' in e && e.button === 2)) {
      return;
    }
    if (this.config.enableSwipe && this.handlers?.onSwipe) {
      this.startTriggerSwipe(e);
    }
    const now = Date.now();
    const isClick = !this.isPinching && !this.rotateStart;
    const distance = Math.sqrt(Math.pow(e.clientX - this.startX, 2) + Math.pow(e.clientY - this.startY, 2));
    const deltaTime = now - this.lastTime;
    const longPressEndTime = now - this.lastlongPressTime;
    const isDistance = distance < this.longPressDistance;
    const isLongPressEndTime = longPressEndTime > this.config.longPressThreshold;
    
    if (this.longPressTimer) {
      clearTimeout(this.longPressTimer);
      this.longPressTimer = null;
    }
    this.handleLongPressEnd(e, isLongPressEndTime, isDistance);
    if (this.isPinching && this.pinchStart) {
      this.endPinch();
    } else if (this.rotateStart) {
      this.endRotate();
    } else if (isClick) {
      this.handleClickEvent(e, distance, now, deltaTime);
      this.lastClickTime = now;
    }
    this.resetState();
  };

  private handlePointerCancel = (e: PointerEvent): void => {
    if (this.handlers?.onLongPressCancel && this.isPressStart) {
      this.isPressStart = false;
      this.handlers.onLongPressCancel(e);
    }
    if (this.longPressTimer) {
      clearTimeout(this.longPressTimer);
      this.longPressTimer = null;
    }
    this.lastPointers = this.lastPointers.filter((p) => p.pointerId !== e.pointerId);
    this.pointerId = null;
  };

  private handleTouchStart = (e: TouchEvent): void => {
    if (e.touches.length === 1) {
      const touch = e.touches[0];
      const fakeEvent = this.createFakePointerEvent('pointerdown', touch);
      this.handlePointerDown(fakeEvent);
      this.isDoubleFingers = false;
    } else {
      this.lastPointers = Array.from(e.touches).map((touch) => this.createFakePointerEvent('pointerdown', touch));
      if (e.touches.length === 2) {
        this.isDoubleFingers = true;
        this.doubleFingerEventStart();
      }
    }
  };

  private handleTouchMove = (e: TouchEvent): void => {
    if (e.touches.length === 1) {
      const touch = e.touches[0];
      this.isDoubleFingers = false;
      const fakeEvent = this.createFakePointerEvent('pointermove', touch);
      this.handlePointerMove(fakeEvent);
    } else {
      this.movePointers = Array.from(e.touches).map((touch) => this.createFakePointerEvent('pointermove', touch));
      if (this.isPinching && e.touches.length === 2) {
        this.isDoubleFingers = true;
        this.doubleFingerEventMove();
      }
    }
  };

  private handleTouchEnd = (e: TouchEvent): void => {
    this.isLongPress = false;
    this.isDoubleFingers = false;
    const changedTouches = Array.from(e.changedTouches);
    for (const touch of changedTouches) {
      const fakeEvent = this.createFakePointerEvent('pointerup', touch);
      this.handlePointerUp(fakeEvent);
    }
  };

  private handlePinchEvent(e: WheelEvent): void {
    this.scaleTimer = window.setTimeout(() => {
      const event = this.createPcInteractionEvent(e);
      event.pinchCenterX = 0;
      event.pinchCenterY = 0;
      this.updateScale = window.devicePixelRatio / this.initialScale;
      event.scale = this.updateScale;
      if (this.handlers?.onPinchStart && !this.pinchStart) {
        this.pinchStart = true;
        this.handlers.onPinchStart(event);
      } else if (this.handlers?.onPinchMove && this.pinchStart) {
        this.handlers.onPinchMove(event);
      }
    }, 50);
  }

  private handleSwipeEvent(e: WheelEvent, deltaY: number): void {
    if (this.config.enableSwipe && this.handlers?.onSwipe) {
      if (this.config.direction === 'All' || this.config.direction === 'Horizontal' || this.config.direction === 'Vertical') {
        const event = this.createPcInteractionEvent(e);
        event.speed = deltaY;
        this.handlers.onSwipe(event);
      }
    }
  }

  private handleScrollEvent(e: WheelEvent, deltaX: number, deltaY: number): void {
    if (this.config.enableScroll && this.handlers?.onScroll) {
      const event = this.createPcInteractionEvent(e);
      event.velocityX = deltaX;
      event.velocityY = deltaY;
      event.velocity = 0;
      event.offsetX = e.deltaX;
      event.offsetY = e.deltaY;
      this.handlers.onScroll(event);
    }
  }

  private handlePinchEventEnd(e: WheelEvent): void {
    if (this.config.enableScale && this.handlers?.onPinchEnd && this.pinchStart) {
      this.pinchStart = false;
      const event = this.createPcInteractionEvent(e);
      event.scale = this.updateScale;
      event.pinchCenterX = 0;
      event.pinchCenterY = 0;
      this.handlers.onPinchEnd(event);
    }
  }

  private handleWheel = (e: WheelEvent): void => {
    const deltaX = e.deltaX;
    const deltaY = e.deltaY;
    if (e.ctrlKey && this.config.enableScale) {
      this.handlePinchEvent(e);
    } else {
      this.handleSwipeEvent(e, deltaY);
      this.handleScrollEvent(e, deltaX, deltaY);
      this.handlePinchEventEnd(e);
      if (this.scaleTimer) {
        clearTimeout(this.scaleTimer);
        this.scaleTimer = null;
      }
    }
  };

  private doubleFingerEventStart(): void {
    if (this.lastPointers.length < 2 && !this.isDoubleFingers) {
      return;
    }
    this.isPinching = true;
    this.startDistance = getPointersDistance(this.lastPointers);
    this.startAngle = getAngle(this.lastPointers[0].clientX, this.lastPointers[0].clientY, this.lastPointers[1].clientX, this.lastPointers[1].clientY);
  }

  private doubleFingerPinchEvent(event: PointerState, scale: number, center: { x: number; y: number }, isMinDistance: boolean): void {
    event.scale = scale;
    event.pinchCenterX = center.x;
    event.pinchCenterY = center.y;
    if (!this.pinchStart && this.handlers?.onPinchStart && isMinDistance && this.executeZoom) {
      this.pinchStart = true;
      this.executeRotate = false;
      this.handlers.onPinchStart(event);
    } else if (this.handlers?.onPinchMove && this.executeZoom && this.pinchStart) {
      this.handlers.onPinchMove(event);
    }
  }

  private doubleFingerRotateEvent(event: PointerState, angleDiff: number, isMinAngle: boolean): void {
    event.angle = angleDiff;
    if (this.handlers?.onRotateStart && !this.rotateStart && isMinAngle && this.executeRotate) {
      this.rotateStart = true;
      this.executeZoom = false;
      this.handlers.onRotateStart(event);
    } else if (this.handlers?.onRotateMove && this.rotateStart && this.executeRotate) {
      this.handlers.onRotateMove(event);
    }
  }

  private doubleFingerEventMove(): void {
    if (this.movePointers.length < 2 || !this.isPinching || !this.isDoubleFingers) {
      return;
    }
    const currentDistance = getPointersDistance(this.movePointers);
    const currentAngle = getAngle(this.movePointers[0].clientX, this.movePointers[0].clientY, this.movePointers[1].clientX, this.movePointers[1].clientY);
    const distanceChange = Math.abs(currentDistance - this.startDistance);
    const scale = currentDistance / this.startDistance;
    const angleDiff = currentAngle - this.startAngle;
    const fingerList = this.getFingerInfo();
    const center = getPointersCenter(this.movePointers);
    const event: PointerState = this.getTouchEvent(this.movePointers[0]);
    event.fingerList = fingerList;
    const isMinDistance = distanceChange > this.config.scaleDistance;
    const isMinAngle = Math.abs(angleDiff) > this.config.angle;
    if (this.config.enableScale) {
      this.doubleFingerPinchEvent(event, scale, center, isMinDistance);
    }
    if (this.config.enableRotate) {
      this.doubleFingerRotateEvent(event, angleDiff, isMinAngle);
    }
  }

  private endPinch(): void {
    const fingerList = this.getFingerInfo();
    if (this.handlers?.onPinchEnd && this.config.enableScale && this.pinchStart) {
      const center = getPointersCenter(this.movePointers);
      const event = this.getTouchEvent(this.movePointers[0]);
      event.fingerList = fingerList;
      event.pinchCenterX = center.x;
      event.pinchCenterY = center.y;
      this.handlers.onPinchEnd(event);
    }
  }

  private endRotate(): void {
    const fingerList = this.getFingerInfo();
    const currentAngle = getAngle(this.movePointers[0].clientX, this.movePointers[0].clientY, this.movePointers[1].clientX, this.movePointers[1].clientY);
    const angleDiff = currentAngle - this.startAngle;
    if (this.config.enableRotate && this.handlers?.onRotateEnd && this.rotateStart) {
      const event = this.getTouchEvent(this.movePointers[0]);
      event.fingerList = fingerList;
      event.angle = angleDiff;
      this.handlers.onRotateEnd(event);
    }
  }

  private handleDragStart(e: DragEvent): void {
    if (e.target && 'interactionEventsInstance' in e.target) {
      const instance = e.target.interactionEventsInstance as InteractionEvents;
      if (instance && instance.handlers?.onDragStart) {
        instance.handlers.onDragStart(e);
      }
    }
  }

  private handleDragEnter(e: DragEvent): void {
    if (e.target && 'interactionEventsInstance' in e.target) {
      const instance = e.target.interactionEventsInstance as InteractionEvents;
      if (instance && instance.handlers?.onDragEnter) {
        instance.handlers.onDragEnter(e);
      }
    }
  }

  private handleDragMove(e: DragEvent): void {
    if (e.target && 'interactionEventsInstance' in e.target) {
      const instance = e.target.interactionEventsInstance as InteractionEvents;
      if (instance && instance.handlers?.onDragMove) {
        instance.handlers.onDragMove(e);
      }
    }
  }

  private handleDragLeave(e: DragEvent): void {
    if (e.target && 'interactionEventsInstance' in e.target) {
      const instance = e.target.interactionEventsInstance as InteractionEvents;
      if (instance && instance.handlers?.onDragLeave) {
        instance.handlers.onDragLeave(e);
      }
    }
  }

  private handleDrop(e: DragEvent): void {
    if (e.target && 'interactionEventsInstance' in e.target) {
      const instance = e.target.interactionEventsInstance as InteractionEvents;
      if (instance && instance.handlers?.onDrop) {
        instance.handlers.onDrop(e);
      }
    }
  }

  private triggerHover = (e: PointerEvent): void => {
    const event = this.createPcInteractionEvent(e);
    if (this.handlers?.onHover) {
      this.isHoverStart = !this.isHoverStart;
      event.stopPropagation = e.stopPropagation;
      this.handlers.onHover(event);
    }
  };

  private triggerHoverMove = (e: PointerEvent): void => {
    const event = this.createPcInteractionEvent(e);
    if (this.handlers?.onHoverMove) {
      event.stopPropagation = e.stopPropagation;
      this.handlers.onHoverMove(event);
    }
  };

  private startTriggerSwipe(e: PointerEvent): void {
    const now = Date.now();
    const deltaTime = now - this.lastTime;
    const deltaX = e.clientX - this.startX;
    const deltaY = e.clientY - this.startY;
    const actualDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
    const actualSpeed = actualDistance / deltaTime;
    const velocityX = deltaX / deltaTime;
    const velocityY = deltaY / deltaTime;
    const angleRad = Math.atan2(deltaY, deltaX);
    const angleDeg = (angleRad * 180) / Math.PI;
    const angleX = Math.abs(angleDeg) > 90 ? 180 - Math.abs(angleDeg) : Math.abs(angleDeg);
    const angleY = Math.abs(Math.abs(angleDeg) - 90);
    const isHorizontal = this.config.direction === 'Horizontal' && angleX <= 45;
    const isVertical = this.config.direction === 'Vertical' && angleY <= 45;
    const isDirection = this.config.direction === 'All' || isHorizontal || isVertical;
    const isMinSpeed = actualSpeed > this.config.speed;
    if (isDirection && isMinSpeed) {
      this.triggerSwipe(e, deltaX, deltaY, velocityX, velocityY, actualSpeed, actualSpeed);
    }
  }

  private triggerClick(e: PointerEvent): void {
    if (this.handlers?.onClick) {
      if (e.type === 'touch') {
        const event = this.createTouchInteractionEvent('click', e);
        this.handlers.onClick(event);
      } else {
        const event = this.createPcInteractionEvent(e);
        this.handlers.onClick(event);
      }
    }
  }

  private triggerDoubleClick(e: PointerEvent): void {
    if (this.handlers?.onDoubleClick) {
      if (e.type === 'touch') {
        const event = this.createTouchInteractionEvent('doubleClick', e);
        this.handlers.onDoubleClick(event);
      } else {
        const event = this.createPcInteractionEvent(e);
        this.handlers.onDoubleClick(event);
      }
    }
  }

  private triggerLongPressStart(e: PointerEvent): void {
    if (this.handlers?.onLongPressStart) {
      this.isPressStart = true;
      if (e.type === 'touch') {
        const event = this.createTouchInteractionEvent('longPressStart', e);
        event.repeat = false;
        this.handlers.onLongPressStart(event);
      } else {
        const event = this.createPcInteractionEvent(e);
        event.repeat = false;
        this.handlers.onLongPressStart(event);
      }
    }
  }
  private triggerLongPressEnd(e: PointerEvent): void {
    if (this.handlers?.onLongPressEnd) {
      this.isPressStart = false;
      if (e.type === 'touch') {
        const event = this.createTouchInteractionEvent('longPressEnd', e);
        event.repeat = false;
        this.handlers.onLongPressEnd(event);
      } else {
        const event = this.createPcInteractionEvent(e);
        event.repeat = false;
        this.handlers.onLongPressEnd(event);
      }
    }
  }

  private triggerSwipe(e: PointerEvent, deltaX: number, deltaY: number, velocityX: number, velocityY: number, velocity: number, speed: number): void {
    if (this.handlers?.onSwipe) {
      if (e.type === 'touch') {
        const event = this.createTouchInteractionEvent('swipe', e);
        event.velocityX = velocityX;
        event.velocityY = velocityY;
        event.velocity = velocity;
        event.speed = speed;
        this.handlers.onSwipe(event);
      }
    }
  }

  private triggerScroll(e: PointerEvent, x: number, y: number, sendX: number, sendY: number, send: number): void {
    const isTriggerScroll = getSwipeDirection(x, y, this.config.scrollDirection);
    if (isTriggerScroll && this.handlers?.onScroll) {
      const event = this.createTouchInteractionEvent('scroll', e);
      event.velocityX = sendX;
      event.velocityY = sendY;
      event.velocity = send;
      event.offsetX = x;
      event.offsetY = y;
      this.handlers.onScroll(event);
    }
  }

  private createPcInteractionEvent(originalEvent: PointerEvent | WheelEvent): PointerState {
    const isPointerEvent = 'pointerType' in originalEvent;
    const fingerList = [];
    const addPointer: FingerInfo = {
      id: 1,
      globalX: originalEvent.clientX,
      globalY: originalEvent.clientY,
      localX: originalEvent.offsetX,
      localY: originalEvent.offsetY,
      displayX: originalEvent.screenX,
      displayY: originalEvent.screenY,
      hand: originalEvent.button === 0 ? 'LEFT' : originalEvent.button === 2 ? 'RIGHT' : 'NONE',
    };
    fingerList.push(addPointer);

    return {
      target: originalEvent.target as HTMLElement,
      source: isPointerEvent ? (originalEvent as PointerEvent).pointerType : 'mouse',
      sourceTool: isPointerEvent ? (originalEvent as PointerEvent).pointerType : 'mouse',
      pressure: isPointerEvent ? (originalEvent as PointerEvent).pressure : 0,
      axisHorizontal: 0,
      axisVertical: 0,
      fingerList: fingerList,
      x: originalEvent.offsetX,
      y: originalEvent.offsetY,
      windowX: originalEvent.clientX,
      windowY: originalEvent.clientY,
      displayX: originalEvent.screenX,
      displayY: originalEvent.screenY,
      hand: originalEvent.button === 0 ? 'LEFT' : originalEvent.button === 2 ? 'RIGHT' : 'NONE',
    };
  }

  private createTouchInteractionEvent(
    pointerType: InteractionEventType,
    originalEvent: PointerEvent
  ): PointerState {
    const event = this.publicDataExtraction(originalEvent);
    if (pointerType === 'click') {
      return {
        displayX: originalEvent.screenX,
        displayY: originalEvent.screenY,
        ...event,
      };
    }
    return { ...event, };
  }

  private publicDataExtraction(e: PointerEvent): PointerState {
    const fingerList = [];
    const offsetX = e.clientX - (e.target as HTMLElement).offsetLeft;
    const offsetY = e.clientX - (e.target as HTMLElement).offsetTop;
    const addPointer: FingerInfo = {
      id: e.pointerId,
      globalX: e.clientX,
      globalY: e.clientY,
      localX: offsetX,
      localY: offsetY,
      displayX: e.screenX,
      displayY: e.screenY,
      hand: 'NONE',
    };
    fingerList.push(addPointer);
    return {
      target: e.target as HTMLElement,
      sourceTool: 'finger',
      source: 'touchScreen',
      hand: 'NONE',
      axisHorizontal: 0,
      axisVertical: 0,
      fingerList: fingerList,
      windowX: e.clientX,
      windowY: e.clientY,
      x: Math.abs(offsetX),
      y: Math.abs(offsetY),
    };
  }

  private createFakePointerEvent(type: string, touch: Touch): PointerEvent {
    return {
      pointerType: type,
      pointerId: 2,
      type: 'touch',
      clientX: touch.clientX,
      clientY: touch.clientY,
      pressure: touch.force,
      pageX: touch.pageX,
      pageY: touch.pageY,
      screenX: touch.screenX,
      screenY: touch.screenY,
      target: touch.target,
      bubbles: true,
      cancelable: true,
      currentTarget: null,
      defaultPrevented: false,
      eventPhase: 0,
      isTrusted: true,
      timeStamp: Date.now(),
    } as PointerEvent;
  }

  private getFingerInfo(): FingerInfo[] {
    let totalX = 0;
    let totalY = 0;
    const fingerList = [];
    for (let i = 0; i < this.movePointers.length; i++) {
      totalX += this.movePointers[i].clientX;
      totalY += this.movePointers[i].clientY;
      const offsetX = this.movePointers[i].clientX - (this.movePointers[i].target as HTMLElement).offsetLeft;
      const offsetY = this.movePointers[i].clientX - (this.movePointers[i].target as HTMLElement).offsetTop;
      let params = {
        id: i,
        globalX: this.movePointers[i].clientX,
        globalY: this.movePointers[i].clientY,
        displayX: this.movePointers[i].screenX,
        displayY: this.movePointers[i].screenY,
        localX: offsetX,
        localY: offsetY,
        hand: 'NONE'
      };
      fingerList.push(params);
    }
    return fingerList;
  }

  private getTouchEvent(touch: PointerEvent): PointerState {
    const event: PointerState = {
      target: this.element,
      windowX: touch.clientX,
      windowY: touch.clientY,
      source: 'touchScreen',
      hand: 'NONE',
      pressure: touch.pressure,
      sourceTool: 'finger',
      axisHorizontal: 0,
      axisVertical: 0
    };
    return event;
  }

  private resetState(): void {
    this.isPointerDown = false;
    this.pointerId = null;
    this.isPinching = false;
    this.executeZoom = true;
    this.executeRotate = true;
    this.pinchStart = false;
    this.rotateStart = false;
    this.movePointers = [];
    this.lastPointers = [];
  }

  destroy(): void {
    this.cleanupEventListeners();
    if (this.longPressTimer) {
      clearTimeout(this.longPressTimer);
      this.longPressTimer = null;
    }
    if ('interactionEventsInstance' in this.element) {
      this.element.interactionEventsInstance = null;
    }
  }
}

export function PointerGestureManager(
  element: HTMLElement,
  handlers: GestureHandlers,
  defaultConfig?: PointerOptions
): InteractionEvents {
  const interactionEvents = defaultConfig ? new InteractionEvents(element, handlers, defaultConfig) : 
  new InteractionEvents(element, handlers);
  (element as any).interactionEventsInstance = interactionEvents;
  return interactionEvents;
}